package com.icongyou.enterprise.talent_analysis.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.icongyou.enterprise.talent_analysis.entity.PortraitCache;
import com.icongyou.enterprise.talent_analysis.entity.PortraitResponse;
import com.icongyou.enterprise.talent_analysis.entity.PortraitUpdateTask;
import com.icongyou.enterprise.talent_analysis.mapper.PortraitCacheMapper;
import com.icongyou.enterprise.talent_analysis.mapper.PortraitUpdateTaskMapper;
import com.icongyou.enterprise.talent_analysis.service.impl.PortraitAsyncServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

/**
 * 画像异步服务测试
 */
@ExtendWith(MockitoExtension.class)
@DisplayName("画像异步服务测试")
class PortraitAsyncServiceImplTest {

    @Mock
    private PortraitUpdateTaskMapper taskMapper;

    @Mock
    private PortraitCacheMapper cacheMapper;

    @Mock
    private PortraitService portraitService;

    @Mock
    private AIPortraitEvaluationService aiPortraitService;

    @Spy
    private ObjectMapper objectMapper = new ObjectMapper();

    @InjectMocks
    private PortraitAsyncServiceImpl portraitAsyncService;

    private PortraitUpdateTask task;
    private PortraitResponse portraitResponse;

    @BeforeEach
    void setUp() {
        task = new PortraitUpdateTask();
        task.setId("task-1");
        task.setStudentId("stu-1001");
        task.setTemplateId("tmpl-2001");
        task.setStatus("PENDING");
        task.setRetryCount(0);
        task.setCreatedAt(LocalDateTime.now());

        portraitResponse = new PortraitResponse();
        portraitResponse.setLabels(Arrays.asList("技术能力", "学习能力"));
        portraitResponse.setScores(Arrays.asList(4.5, 4.0));
        portraitResponse.setDimensions(Collections.emptyList());
        portraitResponse.setAiUsed(true);
    }

    // ==================== triggerStudentPortraitUpdate 测试 ====================

    @Test
    @DisplayName("触发学生画像更新 - 成功创建任务")
    void testTriggerStudentPortraitUpdate_Success() {
        // Given
        when(cacheMapper.deleteByStudentId("stu-1001")).thenReturn(3);
        when(taskMapper.insert(any(PortraitUpdateTask.class))).thenReturn(1);

        // When
        portraitAsyncService.triggerStudentPortraitUpdate("stu-1001", "MANUAL");

        // Then
        verify(cacheMapper).deleteByStudentId("stu-1001");
        
        ArgumentCaptor<PortraitUpdateTask> taskCaptor = ArgumentCaptor.forClass(PortraitUpdateTask.class);
        verify(taskMapper).insert(taskCaptor.capture());
        
        PortraitUpdateTask capturedTask = taskCaptor.getValue();
        assertEquals("stu-1001", capturedTask.getStudentId());
        assertNull(capturedTask.getTemplateId()); // 默认模板
        assertEquals("DATA_CHANGE", capturedTask.getTriggerType());
        assertEquals("MANUAL", capturedTask.getTriggerSource());
        assertEquals("PENDING", capturedTask.getStatus());
        assertEquals(0, capturedTask.getRetryCount());
        assertNotNull(capturedTask.getId());
        assertNotNull(capturedTask.getCreatedAt());
    }

    @Test
    @DisplayName("触发学生画像更新 - 清除缓存失败不影响任务创建")
    void testTriggerStudentPortraitUpdate_CacheDeleteFailed() {
        // Given
        when(cacheMapper.deleteByStudentId("stu-1001")).thenReturn(0);
        when(taskMapper.insert(any(PortraitUpdateTask.class))).thenReturn(1);

        // When
        portraitAsyncService.triggerStudentPortraitUpdate("stu-1001", "AUTO");

        // Then
        verify(cacheMapper).deleteByStudentId("stu-1001");
        verify(taskMapper).insert(any(PortraitUpdateTask.class));
    }

    // ==================== triggerTemplatePortraitUpdate 测试 ====================

    @Test
    @DisplayName("触发模板画像更新 - 清除模板缓存")
    void testTriggerTemplatePortraitUpdate_Success() {
        // Given
        when(cacheMapper.deleteByTemplateId("tmpl-2001")).thenReturn(50);

        // When
        portraitAsyncService.triggerTemplatePortraitUpdate("tmpl-2001");

        // Then
        verify(cacheMapper).deleteByTemplateId("tmpl-2001");
    }

    @Test
    @DisplayName("触发模板画像更新 - 删除0条记录")
    void testTriggerTemplatePortraitUpdate_NoRecords() {
        // Given
        when(cacheMapper.deleteByTemplateId("tmpl-9999")).thenReturn(0);

        // When
        portraitAsyncService.triggerTemplatePortraitUpdate("tmpl-9999");

        // Then
        verify(cacheMapper).deleteByTemplateId("tmpl-9999");
    }

    // ==================== processUpdateTasks 测试 ====================

    @Test
    @DisplayName("处理更新任务 - 无待处理任务")
    void testProcessUpdateTasks_NoTasks() {
        // Given
        when(taskMapper.selectPendingTasks(10)).thenReturn(Collections.emptyList());

        // When
        portraitAsyncService.processUpdateTasks();

        // Then
        verify(taskMapper).selectPendingTasks(10);
        verify(portraitService, never()).computeDefaultPortrait(anyString());
    }

    @Test
    @DisplayName("处理更新任务 - 默认模板成功")
    void testProcessUpdateTasks_DefaultTemplateSuccess() {
        // Given
        task.setTemplateId(null);
        List<PortraitUpdateTask> tasks = Collections.singletonList(task);
        
        when(taskMapper.selectPendingTasks(10)).thenReturn(tasks);
        when(taskMapper.updateToProcessing("task-1")).thenReturn(1);
        when(portraitService.computeDefaultPortrait("stu-1001")).thenReturn(portraitResponse);
        when(cacheMapper.insert(any(PortraitCache.class))).thenReturn(1);
        when(taskMapper.updateToCompleted(any(PortraitUpdateTask.class))).thenReturn(1);

        // When
        portraitAsyncService.processUpdateTasks();

        // Then
        verify(taskMapper).selectPendingTasks(10);
        verify(taskMapper).updateToProcessing("task-1");
        verify(portraitService).computeDefaultPortrait("stu-1001");
        verify(cacheMapper).insert(any(PortraitCache.class));
        verify(taskMapper).updateToCompleted(argThat(t -> 
            "COMPLETED".equals(t.getStatus()) && t.getCompletedAt() != null
        ));
    }

    @Test
    @DisplayName("处理更新任务 - 自定义模板成功")
    void testProcessUpdateTasks_CustomTemplateSuccess() {
        // Given
        List<PortraitUpdateTask> tasks = Collections.singletonList(task);
        
        when(taskMapper.selectPendingTasks(10)).thenReturn(tasks);
        when(taskMapper.updateToProcessing("task-1")).thenReturn(1);
        when(aiPortraitService.computeCustomPortrait("stu-1001", "tmpl-2001"))
            .thenReturn(portraitResponse);
        when(cacheMapper.insert(any(PortraitCache.class))).thenReturn(1);
        when(taskMapper.updateToCompleted(any(PortraitUpdateTask.class))).thenReturn(1);

        // When
        portraitAsyncService.processUpdateTasks();

        // Then
        verify(taskMapper).selectPendingTasks(10);
        verify(aiPortraitService).computeCustomPortrait("stu-1001", "tmpl-2001");
        verify(cacheMapper).insert(any(PortraitCache.class));
        verify(taskMapper).updateToCompleted(any(PortraitUpdateTask.class));
    }

    @Test
    @DisplayName("处理更新任务 - 生成画像失败")
    void testProcessUpdateTasks_GeneratePortraitFailed() {
        // Given
        List<PortraitUpdateTask> tasks = Collections.singletonList(task);
        
        when(taskMapper.selectPendingTasks(10)).thenReturn(tasks);
        when(taskMapper.updateToProcessing("task-1")).thenReturn(1);
        when(aiPortraitService.computeCustomPortrait("stu-1001", "tmpl-2001"))
            .thenThrow(new RuntimeException("AI服务异常"));
        when(taskMapper.updateToFailed(any(PortraitUpdateTask.class))).thenReturn(1);

        // When
        portraitAsyncService.processUpdateTasks();

        // Then
        verify(taskMapper).selectPendingTasks(10);
        verify(aiPortraitService).computeCustomPortrait("stu-1001", "tmpl-2001");
        verify(cacheMapper, never()).insert(any(PortraitCache.class));
        verify(taskMapper).updateToFailed(argThat(t -> 
            "FAILED".equals(t.getStatus()) && 
            t.getRetryCount() == 1 &&
            t.getErrorMessage().contains("AI服务异常")
        ));
    }

    @Test
    @DisplayName("处理更新任务 - 缓存保存失败")
    void testProcessUpdateTasks_CacheSaveFailed() {
        // Given
        task.setTemplateId(null);
        List<PortraitUpdateTask> tasks = Collections.singletonList(task);
        
        when(taskMapper.selectPendingTasks(10)).thenReturn(tasks);
        when(taskMapper.updateToProcessing("task-1")).thenReturn(1);
        when(portraitService.computeDefaultPortrait("stu-1001")).thenReturn(portraitResponse);
        when(cacheMapper.insert(any(PortraitCache.class)))
            .thenThrow(new RuntimeException("数据库连接失败"));
        when(taskMapper.updateToFailed(any(PortraitUpdateTask.class))).thenReturn(1);

        // When
        portraitAsyncService.processUpdateTasks();

        // Then
        verify(taskMapper).updateToFailed(argThat(t -> 
            "FAILED".equals(t.getStatus()) && 
            t.getErrorMessage().contains("数据库连接失败")
        ));
    }

    @Test
    @DisplayName("处理更新任务 - 批量处理多个任务")
    void testProcessUpdateTasks_MultipleTasks() {
        // Given
        PortraitUpdateTask task2 = new PortraitUpdateTask();
        task2.setId("task-2");
        task2.setStudentId("stu-1002");
        task2.setTemplateId(null);
        task2.setStatus("PENDING");
        task2.setRetryCount(0);
        
        List<PortraitUpdateTask> tasks = Arrays.asList(task, task2);
        
        when(taskMapper.selectPendingTasks(10)).thenReturn(tasks);
        when(taskMapper.updateToProcessing(anyString())).thenReturn(1);
        when(portraitService.computeDefaultPortrait("stu-1002")).thenReturn(portraitResponse);
        when(aiPortraitService.computeCustomPortrait("stu-1001", "tmpl-2001"))
            .thenReturn(portraitResponse);
        when(cacheMapper.insert(any(PortraitCache.class))).thenReturn(1);
        when(taskMapper.updateToCompleted(any(PortraitUpdateTask.class))).thenReturn(1);

        // When
        portraitAsyncService.processUpdateTasks();

        // Then
        verify(taskMapper, times(2)).updateToProcessing(anyString());
        verify(portraitService).computeDefaultPortrait("stu-1002");
        verify(aiPortraitService).computeCustomPortrait("stu-1001", "tmpl-2001");
        verify(cacheMapper, times(2)).insert(any(PortraitCache.class));
        verify(taskMapper, times(2)).updateToCompleted(any(PortraitUpdateTask.class));
    }

    @Test
    @DisplayName("处理更新任务 - 一个成功一个失败")
    void testProcessUpdateTasks_MixedResults() {
        // Given
        PortraitUpdateTask task2 = new PortraitUpdateTask();
        task2.setId("task-2");
        task2.setStudentId("stu-1002");
        task2.setTemplateId(null);
        task2.setStatus("PENDING");
        task2.setRetryCount(0);
        
        List<PortraitUpdateTask> tasks = Arrays.asList(task, task2);
        
        when(taskMapper.selectPendingTasks(10)).thenReturn(tasks);
        when(taskMapper.updateToProcessing(anyString())).thenReturn(1);
        
        // task1 成功
        when(aiPortraitService.computeCustomPortrait("stu-1001", "tmpl-2001"))
            .thenReturn(portraitResponse);
        
        // task2 失败
        when(portraitService.computeDefaultPortrait("stu-1002"))
            .thenThrow(new RuntimeException("生成失败"));
        
        when(cacheMapper.insert(any(PortraitCache.class))).thenReturn(1);
        when(taskMapper.updateToCompleted(any(PortraitUpdateTask.class))).thenReturn(1);
        when(taskMapper.updateToFailed(any(PortraitUpdateTask.class))).thenReturn(1);

        // When
        portraitAsyncService.processUpdateTasks();

        // Then
        verify(taskMapper, times(1)).updateToCompleted(any(PortraitUpdateTask.class));
        verify(taskMapper, times(1)).updateToFailed(any(PortraitUpdateTask.class));
    }

    // ==================== cleanupOldTasks 测试 ====================

    @Test
    @DisplayName("清理旧任务 - 成功删除")
    void testCleanupOldTasks_Success() {
        // Given
        when(taskMapper.deleteCompletedBefore(7)).thenReturn(25);

        // When
        portraitAsyncService.cleanupOldTasks();

        // Then
        verify(taskMapper).deleteCompletedBefore(7);
    }

    @Test
    @DisplayName("清理旧任务 - 无记录删除")
    void testCleanupOldTasks_NoRecords() {
        // Given
        when(taskMapper.deleteCompletedBefore(7)).thenReturn(0);

        // When
        portraitAsyncService.cleanupOldTasks();

        // Then
        verify(taskMapper).deleteCompletedBefore(7);
    }

    // ==================== triggerBatchUpdate 测试 ====================

    @Test
    @DisplayName("触发批量更新 - 调用成功")
    void testTriggerBatchUpdate_Success() {
        // When
        portraitAsyncService.triggerBatchUpdate("tmpl-2001", "TEMPLATE_CHANGE");

        // Then - 方法执行完成即可，当前实现为空
        assertDoesNotThrow(() -> 
            portraitAsyncService.triggerBatchUpdate("tmpl-2001", "TEMPLATE_CHANGE")
        );
    }

    // ==================== clearCacheByTemplate 测试 ====================

    @Test
    @DisplayName("按模板清除缓存 - 成功")
    void testClearCacheByTemplate_Success() {
        // Given
        when(cacheMapper.deleteByTemplateId("tmpl-2001")).thenReturn(100);

        // When
        portraitAsyncService.clearCacheByTemplate("tmpl-2001");

        // Then
        verify(cacheMapper).deleteByTemplateId("tmpl-2001");
    }

    @Test
    @DisplayName("按模板清除缓存 - 无记录")
    void testClearCacheByTemplate_NoRecords() {
        // Given
        when(cacheMapper.deleteByTemplateId("tmpl-9999")).thenReturn(0);

        // When
        portraitAsyncService.clearCacheByTemplate("tmpl-9999");

        // Then
        verify(cacheMapper).deleteByTemplateId("tmpl-9999");
    }

    // ==================== 缓存保存测试 ====================

    @Test
    @DisplayName("保存到缓存 - 验证缓存对象字段")
    void testSaveToCache_VerifyFields() {
        // Given
        task.setTemplateId(null);
        List<PortraitUpdateTask> tasks = Collections.singletonList(task);
        
        when(taskMapper.selectPendingTasks(10)).thenReturn(tasks);
        when(taskMapper.updateToProcessing("task-1")).thenReturn(1);
        when(portraitService.computeDefaultPortrait("stu-1001")).thenReturn(portraitResponse);
        when(taskMapper.updateToCompleted(any(PortraitUpdateTask.class))).thenReturn(1);

        ArgumentCaptor<PortraitCache> cacheCaptor = ArgumentCaptor.forClass(PortraitCache.class);
        when(cacheMapper.insert(cacheCaptor.capture())).thenReturn(1);

        // When
        portraitAsyncService.processUpdateTasks();

        // Then
        PortraitCache savedCache = cacheCaptor.getValue();
        assertNotNull(savedCache.getId());
        assertEquals("stu-1001", savedCache.getStudentId());
        assertNull(savedCache.getTemplateId());
        assertTrue(savedCache.getAiUsed());
        assertNotNull(savedCache.getLabelsJson());
        assertNotNull(savedCache.getScoresJson());
        assertNotNull(savedCache.getDimensionsJson());
        assertNotNull(savedCache.getGeneratedAt());
        assertNotNull(savedCache.getUpdatedAt());
        assertEquals(0, savedCache.getVersion());
    }

    @Test
    @DisplayName("保存到缓存 - JSON序列化成功")
    void testSaveToCache_JsonSerialization() throws Exception {
        // Given
        task.setTemplateId(null);
        List<PortraitUpdateTask> tasks = Collections.singletonList(task);
        
        when(taskMapper.selectPendingTasks(10)).thenReturn(tasks);
        when(taskMapper.updateToProcessing("task-1")).thenReturn(1);
        when(portraitService.computeDefaultPortrait("stu-1001")).thenReturn(portraitResponse);
        when(taskMapper.updateToCompleted(any(PortraitUpdateTask.class))).thenReturn(1);

        ArgumentCaptor<PortraitCache> cacheCaptor = ArgumentCaptor.forClass(PortraitCache.class);
        when(cacheMapper.insert(cacheCaptor.capture())).thenReturn(1);

        // When
        portraitAsyncService.processUpdateTasks();

        // Then
        PortraitCache savedCache = cacheCaptor.getValue();
        
        // 验证JSON可以反序列化
        List<?> labels = objectMapper.readValue(savedCache.getLabelsJson(), List.class);
        assertEquals(2, labels.size());
        
        List<?> scores = objectMapper.readValue(savedCache.getScoresJson(), List.class);
        assertEquals(2, scores.size());
    }

    @Test
    @DisplayName("处理任务 - 重试次数递增")
    void testProcessTask_RetryCountIncrement() {
        // Given
        task.setRetryCount(2);
        List<PortraitUpdateTask> tasks = Collections.singletonList(task);
        
        when(taskMapper.selectPendingTasks(10)).thenReturn(tasks);
        when(taskMapper.updateToProcessing("task-1")).thenReturn(1);
        when(aiPortraitService.computeCustomPortrait("stu-1001", "tmpl-2001"))
            .thenThrow(new RuntimeException("失败"));
        
        ArgumentCaptor<PortraitUpdateTask> taskCaptor = ArgumentCaptor.forClass(PortraitUpdateTask.class);
        when(taskMapper.updateToFailed(taskCaptor.capture())).thenReturn(1);

        // When
        portraitAsyncService.processUpdateTasks();

        // Then
        PortraitUpdateTask failedTask = taskCaptor.getValue();
        assertEquals(3, failedTask.getRetryCount()); // 2 + 1
        assertEquals("FAILED", failedTask.getStatus());
    }
}
